package sketch.generator;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;

import org.apache.commons.lang.StringUtils;

import sketch.util.Checker;


public class SubsetValueGenerator<T> extends ValueGenerator<T> {
	
	public final List<T> raw_data;
	
	protected final int raw_data_size;
	protected final int subset_num;
	
	protected List<T> current_data = null;
	protected int current_set_index = 0;
	
	public SubsetValueGenerator(T...ts) {
		raw_data = new ArrayList<T>();
		for(T t : ts) {
			raw_data.add(t);
		}
		this.raw_data_size = ts.length;
		//compute 2^n
		this.subset_num = this.power2(this.raw_data_size);
	}
	
	public SubsetValueGenerator(Collection<T> ts) {
		raw_data = new ArrayList<T>();
		raw_data.addAll(ts);
		this.raw_data_size = ts.size();
		//compute 2^n
		this.subset_num = this.power2(this.raw_data_size);
	}
	
	public List<T> currentValue() {
		return current_data;
	}
	
	public boolean hasNextValue() {
		return this.current_set_index < this.subset_num;
	}
	
	public List<T> nextValue() {
		//create it and assign to the current data
		List<T> next_subset = this.generate_subset();
		this.current_data = next_subset;
		this.current_set_index++;
		return next_subset;
	}
	
	@Override
	public void reset() {
		super.reset();
		//more here
		this.current_data = null;
		this.current_set_index = 0;
	}
	
	@Override
	public T composeData() {
		throw new UnsupportedOperationException("The compose data method in SubsetSequentialValueGenerator is not supported!");
	}

	@Override
	public boolean hasNext() {
		//throw new UnsupportedOperationException("The hasNext method in SubsetSequentialValueGenerator is not supported!");
		return this.hasNextValue();
	}

	@Override
	protected T generateNext() {
		throw new UnsupportedOperationException("The generateNext method in SubsetSequentialValueGenerator is not supported!");
	}

	int power2(int x) {
		int result = 1;
		for(int i = 0; i < x; i++) {
			result = result*2;
		}
		return result;
	}
	
	List<T> generate_subset() {
		List<T> ret = new LinkedList<T>();
		
		//get the index first
		List<Integer> indices = this.get_index_list();
		Collections.sort(indices);
		int currentIndex = -1;
		for(Integer i : indices) {
			Checker.checkTrue(i > currentIndex, "i: " + i + ", current index: " + currentIndex);
			Checker.checkTrue(i >= 0 && i < this.raw_data_size, "i: " + i+ ", raw data size: " + this.raw_data_size);
			
			ret.add(this.raw_data.get(i));
			currentIndex = i;
		}
		
		return ret;
	}
	
	List<Integer> get_index_list() {
		List<Integer> retIndex = new LinkedList<Integer>();
		if(this.raw_data_size == 0) {
			return retIndex;
		}
		//break the current value into a binary format
		int index = this.current_set_index;
		String binary = Integer.toBinaryString(index);
		Checker.checkTrue(binary.length() <= this.raw_data_size, "binary is: " + binary + ", raw_data_size: " + this.raw_data_size);
		binary = StringUtils.reverse(binary);
		for(int i = 0; i < binary.length(); i++) {
			if(binary.charAt(i) == '1') {
				retIndex.add(i);
			}
		}
		return retIndex;
	}

	@Override
	public List<T> nextWrappedValue() {
		List<T> listT = this.nextValue();
		return listT;
	}
}
